/**
  * Renders transparent 3D cube using OpenVG.
  * Each cube face is rendered as a dynamically updated OpenVG path
  * and shading is created using radial gradient.
  *
  */

#include "game.h"
#include <QMatrix4x4>
#include <QMap>

// for readability, the screen and texture sizes are hard-coded
const float SCREEN_WIDTH=640.0f;
const float SCREEN_HEIGHT=360.0f;

const VGfloat backgroundColor[4] = { 0.0f, 0.0f, 0.0f, 0.0f };

#define SIDES 6
#define QUAD 4

// cube front face color
const VGfloat stops[]=
{
    // pos,r,g,b,a,
    0.0, 1.0, 1.0, 1.0, 1,
    0.15, 1.0, 1.0, 1.0, 1,
    0.2, 1.0, .60, .50, 1,
    0.4, .90, .20, .15, .75f,
    0.8, .24, .10, .10, .50f,
    1.0, .10, .10, .10, .25f,
};
const VGint numStops = sizeof(stops) / sizeof(VGfloat);

const QVector3D normals[SIDES]={
    QVector3D(0.0, 0.0, 1.0),
    QVector3D(1.0, 0.0, 0.0),
    QVector3D(0.0, 0.0, -1.0),
    QVector3D(-1.0, 0.0, 0.0),
    QVector3D(0.0, -1.0, 0.0),
    QVector3D(0.0, 1.0, 0.0),
};

const QVector3D vertices[SIDES*QUAD]={
    QVector3D(-1.0, -1.0, 1.0),
    QVector3D(1.0, -1.0, 1.0),
    QVector3D(1.0, 1.0, 1.0),
    QVector3D(-1.0, 1.0, 1.0),

    QVector3D(1.0, -1.0, 1.0),
    QVector3D(1.0, -1.0, -1.0),
    QVector3D(1.0, 1.0, -1.0),
    QVector3D(1.0, 1.0, 1.0),

    QVector3D(1.0, -1.0, -1.0),
    QVector3D(-1.0, -1.0, -1.0),
    QVector3D(-1.0, 1.0, -1.0),
    QVector3D(1.0, 1.0, -1.0),

    QVector3D(-1.0, -1.0, -1.0),
    QVector3D(-1.0, -1.0, 1.0),
    QVector3D(-1.0, 1.0, 1.0),
    QVector3D(-1.0, 1.0, -1.0),

    QVector3D(-1.0, -1.0, -1.0),
    QVector3D(1.0, -1.0, -1.0),
    QVector3D(1.0, -1.0, 1.0),
    QVector3D(-1.0, -1.0, 1.0),

    QVector3D(-1.0, 1.0, 1.0),
    QVector3D(1.0, 1.0, 1.0),
    QVector3D(1.0, 1.0, -1.0),
    QVector3D(-1.0, 1.0, -1.0),
};

const VGubyte quadCommands[]={
    VG_MOVE_TO_ABS,
    VG_LINE_TO_ABS,
    VG_LINE_TO_ABS,
    VG_LINE_TO_ABS,
    VG_CLOSE_PATH
};

const int numQuadCommands = sizeof(quadCommands)/sizeof(quadCommands[0]);

VGPath sides[SIDES];
VGPaint colors[SIDES];

// temp buffer for transformed path coordinates
VGfloat pathVertices[QUAD*2];

VGfloat radial[5] = { 0, 0, 0, 0, 400}; // focal point x,y are dynamically updated.  radius defines the spread.

/** walltime */
float t;

void game_init()
{
}

void game_destroy()
{
}

void game_prepare()
{
    vgSeti(VG_RENDERING_QUALITY, VG_RENDERING_QUALITY_BETTER);
    for(int i=0;i<SIDES;i++)
    {
        colors[i] = vgCreatePaint();
        vgSetParameteri(colors[i], VG_PAINT_TYPE, VG_PAINT_TYPE_RADIAL_GRADIENT);
        vgSetParameterfv(colors[i], VG_PAINT_RADIAL_GRADIENT, 5, radial);
        vgSetParameterfv(colors[i], VG_PAINT_COLOR_RAMP_STOPS, numStops, stops);
        vgSetParameteri(colors[i], VG_PAINT_COLOR_RAMP_SPREAD_MODE, VG_COLOR_RAMP_SPREAD_PAD);

        sides[i]= vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1.0f, 0.0f, 0, 0, VG_PATH_CAPABILITY_ALL);
        vgAppendPathData(sides[i], numQuadCommands, quadCommands, pathVertices);
    }
}

void game_release()
{
    for(int i=0;i<SIDES;i++)
    {
        vgDestroyPath(sides[i]);
        vgDestroyPaint(colors[i]);
    }
}

void game_update(float walltime)
{
    t=walltime;
}

void game_render()
{
    // clear background
    vgSetfv(VG_CLEAR_COLOR, 4, backgroundColor);
    vgClear(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);

    // compute 3d perspective transformation matrix
    QMatrix4x4 projection;
    projection.setToIdentity();
    projection.perspective(50, 1.0, 1.0, 10.0);
    projection.translate(0,0,4);
    projection.scale(200,200);

    // compute model transformation matrix
    QMatrix4x4 modelview;
    modelview.setToIdentity();
    modelview.rotate(t*45, QVector3D(0,0,1));
    modelview.rotate(t*33, QVector3D(0,1,0));
    modelview.rotate(t*22, QVector3D(1,0,0));

    // center to screen
    vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);
    vgTranslate(SCREEN_WIDTH/2, SCREEN_HEIGHT/2);

    // buffer for face depth sorting
    QMap<qreal, int> sortMap;

    // compute matrix for per vertex calculations
    QMatrix4x4 m=projection*modelview;
    for(int c=0;c<SIDES;c++)
    {
        qreal averagez = 0;
        for(int i=0;i<QUAD;i++)
        {
            QVector3D p = m*vertices[i+c*QUAD];
            pathVertices[i*2]=p.x();
            pathVertices[i*2+1]=p.y();
            averagez += p.z();
        }
        // update VG path coordinates
        vgModifyPathCoords(sides[c], 0, 4, pathVertices);

        // rotate face normal for gradient effect
        QVector3D n=modelview*normals[c];
        radial[2] = n.x()*50;
        radial[3] = n.y()*50;
        vgSetParameterfv(colors[c], VG_PAINT_RADIAL_GRADIENT, 5, radial);

        // create unique sort key for face
        averagez=averagez*1000+c;
        sortMap[averagez] = c;
    }


    // draw quad faces in sorted order
    foreach(int c, sortMap)
    {
        vgSetPaint(colors[c], VG_FILL_PATH);
        vgDrawPath(sides[c], VG_FILL_PATH);
    }
}

void game_event(int type, float x, float y, float z)
{
}
